﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GeoUtils
{
    //The class to describe a geo-entity, such as province, city, 
    //county, village, town, road, address
    public class GeoEntity
    {
        private string strName; //geo-entity name
        private string strTag; //geo-entity tag from data source provider
        public bool bRewritten; //whether the strName is rewritten name
        //the admin codes that the geo-entity belongs to. 
        //One geo-entity may belongs to more than one admin code, such as road type
        private List<int> adminCodeList; 
        
        private int iAdminPreCode; //The admin code that the geo-entity's parent belongs to
        public int iLevel; //The geo-entity's level, 1 - Province, 2 - City, 3 - County, 4 - Town, 5 - Village, 6 - Road, 7 - Zone
        private LatLong latLong; //The center lat/long of the geo-entity
        private BoundPolygon boundPoly; //The bounding poly of the geo-entity
        private BoundRect boundRect; //The bounding rect of the geo-entity

        public string Tag
        {
            get { return strTag; }
            set { strTag = value; }
        }

        public List<int> AdminCodeList
        {
            get { return adminCodeList; }
        }

        public int AdminPreCode
        {
            get { return iAdminPreCode; }
        }

        public LatLong LatLong
        {
            get { return latLong; }
        }

        public BoundPolygon BoundingPolygon
        {
            get { return boundPoly; }
        }

        public BoundRect BoundingRect
        {
            get { return boundRect; }
        }

        public int AdminCode
        {
            get {
                if (adminCodeList != null && adminCodeList.Count > 0)
                {
                    return adminCodeList[0];
                }
                else
                {
                    return iAdminPreCode;
                }
            }
        }

        public string Name
        {
            get { return strName; }
        }

        public bool ContainsPoint(LatLong point)
        {
            if (boundRect.IsPointIn(point) == true)
            {
                return boundPoly.ContainsPoint(point);
            }

            return false;
        }

        public GeoEntity(string strName, string strTag, bool bRew, List<int> adminCodeList, int iAdminPreCode, int level, LatLong latLong, BoundPolygon bp)
        {
            this.strName = strName;
            this.strTag = strTag;
            this.bRewritten = bRew;
            this.adminCodeList = adminCodeList;
            this.iAdminPreCode = iAdminPreCode;
            this.iLevel = level;
            this.latLong = latLong;
            this.boundPoly = bp;
            this.boundRect = null;

            if (bp != null)
            {
                double minLatitude = double.MaxValue;
                double minLongitude = double.MaxValue;
                double maxLatitude = double.MinValue;
                double maxLongitude = double.MinValue;

                // Determine the bounding rectangle of the region.
                for (int i = 0; i < bp.Latitudes.GetLength(0); i++)
                {
                    if (bp.Latitudes[i].Length != bp.Longitudes[i].Length)
                    {
                        throw new Exception("Parameters is invalidated");
                    }
                    for (int j = 0; j < bp.Latitudes[i].Length; j++)
                    {
                        minLatitude = Math.Min(minLatitude, bp.Latitudes[i][j]);
                        minLongitude = Math.Min(minLongitude, bp.Longitudes[i][j]);
                        maxLatitude = Math.Max(maxLatitude, bp.Latitudes[i][j]);
                        maxLongitude = Math.Max(maxLongitude, bp.Longitudes[i][j]);
                    }
                }

                boundRect = new BoundRect(
                    new LatLong(maxLatitude, maxLongitude),
                    new LatLong(minLatitude, minLongitude));

            }
        }

        public bool IsEqual(GeoEntity geoItem)
        {
            if (geoItem == this)
            {
                return true;
            }

            if (geoItem.strName == strName &&
                geoItem.latLong.Latitude == latLong.Latitude &&
                geoItem.latLong.Longitude == latLong.Longitude)
            {
                return true;
            }

            return false;
        }

        public override string ToString()
        {
            return strName + "|" + AdminCode.ToString();
        }
    }
}
